/*
    UIController
        :This is the main router for requests to the other layers (Logic and Storage)
*/

//this should be a static class

//import only what we need
import java.util.ArrayList;
import java.util.Date;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.PrintStream;

public class UIController {

	//create a static instance of ProgFrame
	public static ProgFrame progframe;
	//create an instance of LogicController to access lower layers
	public static LogicController logic;
	//create a static instance of the InterceptStream
	public static InterceptStream inter;


	//our entry point
	public static void main(String[] args) {

		start();

	}

	//add a method to instantiate our progframe
	public static void start() {
		
		//attempt to intercept standard output stream
		PrintStream pstr = new PrintStream(System.out);
		inter = new InterceptStream(pstr, false);
		try{
			System.setOut(inter);
		}catch (SecurityException EXC){
			System.out.println("UICONTROLLER: failed to override standard output stream...");
		}
		logic = new LogicController();
		progframe = new ProgFrame();
		UIController.changeState("MainView");
		System.out.println("UICONTROLLER : starting");
		
	}

	//add a method to exit the program
	public static void exit() {

		System.out.println("UICONTROLLER : exiting");
		System.exit(0);

	}
	
	//route debugging or misc messages to the internal console
	public static void printConsole(String detail) {
		
		if (progframe != null && progframe.layers != null && progframe.layers.consoleReady()){
			progframe.layers.messageConsole(detail);
		}
		
	}
	
	//route information messages to the internal popup
	public static void printPopup(String detail, double seconds) {
		
		if (progframe != null && progframe.layers != null && progframe.layers.popupReady()){
			progframe.layers.messagePopup(detail, seconds);
		}
		
	}

	//tell ProgFrame to change what it is displaying and the mode it is in
	public static boolean changeState(String newstate) {

		System.out.println("UICONTROLLER : changing the state to " + newstate);
		UIController.printPopup("Loading page...", 1.5);
		return progframe.changeState(newstate);

	}

	//ask ProgFrame for an instance to one of its views
	public static Object grabState(String state) {

		System.out.println("UICONTROLLER : grabbing the state of " + state);
		return progframe.getViewPanel(state);

	}

	//ask Logic to find games that the 2 users have played together
	// returns a list of dates for when the games were saved
	public static ArrayList<Date> getGames(String usr1, String usr2) {

		ArrayList<Date> playedgames = new ArrayList<Date>();

		playedgames = logic.getGames(usr1, usr2);

		//default for testing
		System.out.println("UICONTROLLER : getting games dates that " + usr1 + " & " + usr2 + " played together.");

		return playedgames;

	}

	//tells logic to try and load the game linked to the specified date
	// returns true for successful load or false for fail
	public static boolean loadGame(Date gamerep, ArrayList<String> usernames) {

		System.out.println("UICONTROLLER : loading game from " + gamerep);

		return logic.loadGame(gamerep, usernames);

	}

	//asks Logic for the current player
	public static String getGamePlayer() {

		System.out.println("UICONTROLLER : returning game player ");

		return logic.getGamePlayer();

	}

	//asks Logic for the first player
	public static String getFirstPlayer() {

		System.out.println("UICONTROLLER : returning first player ");

		return logic.getFirstPlayer();

	}

	//asks Logic for the board size
	public static Dimension getBoardSize() {

		System.out.println("UICONTROLLER : returning board size ");

		return logic.getBoardSize();

	}


	//tells logic to try and load the current game being played
	// returns true for successful load or false for fail
	public static boolean saveGame() {

		System.out.println("UICONTROLLER : saving game data...");

		return logic.saveGame();

	}

	//ask Logic for the list of usernames of all saved accounts
	public static ArrayList<String> getUsers() {

		System.out.println("UICONTROLLER : getting all saved account usernames");

		return logic.getUsers();

	}

	//ask Logic for the statistics of a certain player specififed by the username
	public static ArrayList<Integer> getUserStats(String username) {

		System.out.println("UICONTROLLER : getting stats for " + username);

		return logic.getPlayerStats(username);

	}

	//tell logic to clear the stats for a specific account specified by the username
	public static boolean deleteUserStats(String username) {

		System.out.println("UICONTROLLER : deleting stats for " + username);

		logic.deletePlayerStats(username);
		return true;

	}

	//tell logic to clear the stats for every saved account
	public static boolean deleteAllStats() {

		System.out.println("UICONTROLLER : deleting stats for all accounts");

		logic.deleteAllStats();
		return true;

	}

	//tell logic to delete the account specified by the username
	public static boolean deleteUser(String username) {

		System.out.println("UICONTROLLER : deleting account of " + username);

		logic.deletePlayer(username);
		return true;

	}


	//requested by Vince
	/*
	setIB (Set initial boards).
		This will be called by the GUI when game setup is complete.
		At this point I will set the intial boards within the MovesLog.
		So i just need you to call it when the transition between game setup and game play occures.
	*/
	public static void initializeBoards() {

		System.out.println("UICONTROLLER : initializing boards");

		logic.setIB();

	}

	//tell Logic to add the specified piece to the game
	// "PieceName,OwnerName,XCoord,YCoord,ZCoord"
	public static boolean addPiece(String piece) {

		System.out.println("UICONTROLLER : adding piece: " + piece);

		//logic returns a boolean object but we only need a boolean value
		return logic.addPiece(piece).booleanValue();

	}

	//tell Logic to get the board info for the board number we received
	// returns a list of pieces as "PieceName,OwnerName,XCoord,YCoord,ZCoord"
	public static ArrayList<String> getBoard(int number) {

		System.out.println("UICONTROLLER : getting board data " + number);

		return logic.getBoard(number);

	}

	//tell Logic to get the initial board states stored for replays
	// returns a list of pieces as "PieceName,OwnerName,XCoord,YCoord,ZCoord"
	public static ArrayList<String> getReplayBoards() {

		System.out.println("UICONTROLLER : getting replay boards");

		return logic.getReplayBoards();

	}

	//tell Logic to get the list of moves stored for replays
	// returns a list of turns each of which has a list of moves defined as
	// { "mine_hit", "change_to_king", "initial_position", ["jumped_position" ... ] , "destination_position" }
	public static ArrayList<ArrayList<ArrayList<String>>> getReplayMoves() {

		System.out.println("UICONTROLLER : getting replay moves");

		return logic.getReplayMoves();

	}
	
	
	//tell Logic to get the current game play statistics
	// returns a list of player gameplay stats each of which has a list of integers defined as
	// { movestotal, warps, jumps, kingscreated, kingsdestroyed }
	public static ArrayList<ArrayList<Integer>> getStats() {
		
		System.out.println("UICONTROLLER : getting game play stats");
		
		return logic.getStats();
		
	}


	//tell Logic to get moves this piece can make
	//returns a list of move destinations as "XCoord,YCoord,ZCoord"
	public static ArrayList<String> getAvailableMoves(String piece) {

		System.out.println("UICONTROLLER : getting test available moves using " + piece);

		return logic.getAvailableMoves(piece);

	}

	//tell Logic to get the pieces that can be moved
	//returns move in the format: "PieceType,PieceOwner,x,y,z"
	public static ArrayList<String> getMovablePieces() {

		System.out.println("UICONTROLLER : getting movable pieces");

		return logic.getMovablePieces();

	}

	//tell Logic to add the specified number of boards of the specified size
	public static boolean addBoards(int boards, Dimension size) {

		System.out.println("UICONTROLLER : adding " + boards + " boards of size " + size);

		return logic.addBoards(boards, size);

	}

	//tell Logic to make the specified move
	//returns a list of Strings that indicate important board properties
	// { "hitMine", "changetoKing", [removepiece:"x,y,z",...,] }
	public static ArrayList<String> makeMove(ArrayList<Point> coords, ArrayList<Integer> boards, String piece) {

		//Logic asks for list of booleans representing special move properties
		// but GUI can't calculate that wihtout rules in the current configuration
		//make dummy jumps list
		ArrayList<Boolean> specials = new ArrayList<Boolean>();
		for (int i=0; i<coords.size(); ++i){
			specials.add(new Boolean(false));
		}
		System.out.println("UICONTROLLER : making move using piece " + piece);
		//logic returns String list representing unique move attributes
		//{ <boolean>landed_on_mine, <boolean>converted_to_king, [optional]<String>location_jumped:{<int>x, <int>y, <int>board} }
		return logic.makeMove(coords, boards, specials, piece);

	}

	//tell Logic to simulate a coin toss and tell us the result
	public static boolean coinToss(int side) {

		System.out.println("UICONTROLLER : tossing coin");

		return logic.coinToss(side);

	}

	//tell Logic to randomize the placement of pieces
	public static void randomizePlacement() {

		System.out.println("UICONTROLLER : randomize placement of pieces");

		logic.randomizePlacement();

	}

	//convenience method to set the logged players (support "go back" in login)
	//tell logic we need to start a new game
	public static void setGamePlayers(ArrayList<String> players) {
		
		System.out.println("UICONTROLLER : setting game players");
		
		//seems silly, but send the players back into GUI for GameView
		((GameView)UIController.grabState("GameView")).gameboard.setGamePlayers(players);
		//tell logic to start a new game
		UIController.newGame();
		logic.setGamePlayers(players);
		
	}
	
	
	//convenience method to get the logged players (support proper gameload)
	public static ArrayList<String> getGamePlayers() {
		
		System.out.println("UICONTROLLER : getting game players");
		
		return logic.getGamePlayers();
		
	}


	//tell logic to authenticate the matching of this username and password in the saved user accounts
	public static boolean authenticateUser(String username, String password) {

		System.out.println("UICONTROLLER : Authenticate account: username=" + username + " - password=" + password);

		boolean authenticated = false;
		authenticated = logic.authenticatePlayer(username, password);

		return authenticated;

	}

	//tell logic to try and create a new account in the saved user accounts with this username and password
	public static boolean createUser(String username, String password) {

		System.out.println("UICONTROLLER : Create account: username=" + username + " - password=" + password);

		return logic.savePlayer(username, password);

	}

	//tell logic that the specified player wants to forfeit the game
	public static void forfeitGame(String player) {
		
		System.out.println("UICONTROLLER : Forfeit game");
		logic.finishGame(player);
		
	}
	
	//tell logic that the specified player has lost the game
	public static void loseGame(String player) {
		
		System.out.println("UICONTROLLER : Lose game");
		logic.finishGame(player);
		
	}
	
	
	//tell logic that both plyers want to call a draw for the game
	public static void callDraw() {
	
		System.out.println("UICONTROLLER : Tie game");
		logic.finishGame("");
	
	}

	//tell logic we need to start a new game
	public static void newGame() {

		System.out.println("UICONTROLLER : New Game!!!");

		logic.newGame();

	}

	//tell Logic to retrieve the piece counts for the current player
	public static ArrayList<Integer> getPieceCounts() {

		System.out.println("UICONTROLLER : getting remaining piece counts");

		return logic.getPieceCounter();

	}


	//this is a convenience method for all GUI components to use if needed
	// "PieceName,OwnerName,XCoord,YCoord,ZCoord"
	public static void convertPieceToString(String intype, String inowner, Integer inx, Integer iny, Integer inz, ArrayList<String> outstr) {

		outstr.add(new String(intype + "," + inowner + "," + inx + "," + iny + "," + inz));
		System.out.println("UICONTROLLER : convert piece 2 string -> given ["
			+ intype + "][" + inowner + "][" + inx + "][" + iny + "][" + inz + "] returning ["
			+ outstr.get(outstr.size()-1) + "]");

	}
	//this is a convenience method for all GUI components to use if needed
	// "PieceName,OwnerName,XCoord,YCoord,ZCoord"
	public static void convertStringToPiece(String instr, ArrayList<String> outtype, ArrayList<String> outowner, ArrayList<Integer> outcoords) {

		//System.out.println("convertStringToPiece: " + instr);
		String[] splits = instr.split("\\,");
		outtype.add(splits[0]);
		outowner.add(splits[1]);
		Integer outx = new Integer(0);
		Integer outy = new Integer(0);
		Integer outz = new Integer(0);
		try{
			outx = new Integer(Integer.parseInt(splits[2]));
			outy = new Integer(Integer.parseInt(splits[3]));
			outz = new Integer(Integer.parseInt(splits[4]));
		}catch (NumberFormatException EXC){}
		outcoords.add(outx);
		outcoords.add(outy);
		outcoords.add(outz);
		System.out.println("UICONTROLLER : convert string 2 piece -> given ["
			+ instr + "] returning ["
			+ outtype.get(outtype.size()-1) + "][" + outowner.get(outowner.size()-1) + "]["
			+ outcoords.get(outcoords.size()-3) + "][" + outcoords.get(outcoords.size()-2) + "]["
			+ outcoords.get(outcoords.size()-1) + "]");

	}

	//internal class used as a default image
	// when an existing image fails to load
	public static class NotFoundImage extends BufferedImage {

		//default constructor
		public NotFoundImage(int width, int height) {

			//call our ancestor
			super(width, height, BufferedImage.TYPE_INT_ARGB);
			//draw image
			this.drawDefault();

		}


		//hidden method for drawing the image
		private void drawDefault() {

			//obtain a graphics instance of this image
			Graphics g = super.getGraphics();
			//discover what scale to draw
			double scalewidth = super.getWidth() / 100.0;
			double scaleheight = super.getHeight() / 100.0;
			//draw a white background
			g.setColor(Color.WHITE);
			g.fillRect(0, 0, (int)(scalewidth * 100), (int)(scaleheight * 100));
			//draw a red X based on the following points:
			int[] xpoints = { 0, 25, 50, 75, 100, 75, 100, 75, 50, 25, 0, 25 };
			int[] ypoints = { 25, 0, 25, 0, 25, 50, 75, 100, 75, 100, 75, 50 };
			for (int i=0; i<xpoints.length; ++i){
				xpoints[i] = (int)(xpoints[i] * scalewidth);
				ypoints[i] = (int)(ypoints[i] * scaleheight);
			}
			g.setColor(Color.RED);
			g.fillPolygon(new Polygon(xpoints, ypoints, xpoints.length));

		}

		//override scale image method because that is what we will be using
		public Image getScaledInstance(int newheight, int newwidth, int newhints) {

			//draw image
			this.drawDefault();
			//scale the image that has now been drawn
			return super.getScaledInstance(newheight, newwidth, newhints);

		}

	}

}